Download this file

363 lines (252 with data), 10.9 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
import os
import cv2
import math
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
from sklearn.cluster import KMeans
from skimage.morphology import erosion, opening, closing, square, \
disk, convex_hull_image
from skimage.measure import label, regionprops
SMALL_FONT = 13
MEDIUM_FONT = 15
LARGE_FONT = 18
plt.rc('font', size=SMALL_FONT) # controls default text sizes
plt.rc('axes', titlesize=SMALL_FONT) # fontsize of the axes title
plt.rc('axes', labelsize=MEDIUM_FONT) # fontsize of the x and y labels
plt.rc('xtick', labelsize=SMALL_FONT) # fontsize of the tick labels
plt.rc('ytick', labelsize=SMALL_FONT) # fontsize of the tick labels
plt.rc('legend', fontsize=MEDIUM_FONT) # legend fontsize
plt.rc('figure', titlesize=LARGE_FONT) # fontsize of the figure title
plt.rcParams["figure.figsize"] = (10, 5)
def readSortedSlices(path):
slices = []
for s in os.listdir(path):
slices.append(path + '/' + s)
slices.sort(key = lambda s: int(s[s.find('_') + 1 : s.find('.')]))
ID = slices[0][slices[0].find('/') + 1 : slices[0].find('_')]
print('CT scan of Patient %s consists of %d slices.' % (ID, len(slices)))
return (slices, ID)
def getSliceImages(slices):
return list(map(readImg, slices))
def readImg(path, showOutput=0):
img = cv2.imread(path)
img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
if showOutput:
plt.title('A CT Scan Image Slice')
plt.imshow(img, cmap='gray')
return img
def imgKMeans(img, K, showOutput=0, showHistogram=0):
'''
Apply KMeans on an image with the number of clusters K
Input: Image, Number of clusters K
Output: Dictionary of cluster center labels and points, Output segmented image
'''
imgflat = np.reshape(img, img.shape[0] * img.shape[1]).reshape(-1, 1)
kmeans = KMeans(n_clusters=K, verbose=0)
kmmodel = kmeans.fit(imgflat)
labels = kmmodel.labels_
centers = kmmodel.cluster_centers_
center_labels = dict(zip(np.arange(K), centers))
output = np.array([center_labels[label] for label in labels])
output = output.reshape(img.shape[0], img.shape[1]).astype(int)
# print(len(labels), 'Labels: \n', labels)
# print(len(centers), 'Centers: \n', centers)
# print('Center labels and their points:', center_labels)
if showOutput:
fig, axes = plt.subplots(1, 2, sharex=True, sharey=True, figsize=(10, 5))
axes = axes.ravel()
axes[0].imshow(img, cmap='gray')
axes[0].set_title('Original Image')
axes[1].imshow(output)
axes[1].set_title('Image after KMeans (K = ' + str(K) + ')')
return center_labels, output
def preprocessImage(img, showOutput=0):
'''
Preprocess the image by applying truncated thresholding using KMeans
Input: Image
Output: Preprocessed image
'''
centroids, segmented_img = imgKMeans(img, 3, showOutput=showOutput)
sorted_center_values = sorted([i[0] for i in centroids.values()])
threshold = (sorted_center_values[-1] + sorted_center_values[-2]) / 2
retval, procImg = cv2.threshold(img, threshold, 255, cv2.THRESH_TOZERO)
if showOutput:
fig, axes = plt.subplots(1, 2, sharex=True, sharey=True, figsize=(10, 5))
axes = axes.ravel()
axes[0].imshow(img, cmap='gray')
axes[0].set_title('Original Image')
axes[1].imshow(procImg, cmap='gray')
axes[1].set_title('Processed Image - After Thresholding')
return procImg
def chullForegroundMask(img, showOutput=0):
'''
Generate a convex hull of foreground mask
Input: Whole chest CT image in grayscale format (512x512)
Output: Convex hull of foreground mask in binary format (512x512)
'''
centroid_clusters, segmented_img = imgKMeans(img, 2, showOutput=showOutput)
fg_threshold = sum(centroid_clusters.values())[0] / 2
retval, fg_mask = cv2.threshold(img, fg_threshold, 255, cv2.THRESH_BINARY)
fg_mask_opened = opening(fg_mask, square(3))
fg_mask_opened2 = opening(fg_mask_opened, disk(4))
fg_mask = fg_mask_opened2
ch_fg_mask = convex_hull_image(fg_mask)
if showOutput:
fig, axes = plt.subplots(1, 3, sharex=True, sharey=True, figsize=(16, 10))
axes = axes.ravel()
axes[0].set_title('On Opening with Square SE (3)')
axes[0].imshow(fg_mask_opened, cmap='gray')
axes[1].set_title('On Opening with Disk SE (4)')
axes[1].imshow(fg_mask_opened2, cmap='gray')
axes[2].set_title('Convex Hull of Foreground Mask')
axes[2].imshow(ch_fg_mask, cmap='gray')
return fg_mask, ch_fg_mask, fg_threshold
def chullLungMask(img, ch_fg_mask, fg_threshold, showOutput=0):
'''
Generate a convex hull of lung mask
Input: Whole chest CT image in grayscale format (512x512),
Convex hull of foreground mask in binary format (512x512)
Output: Convex hull of lung mask in binary format (512x512),
Intermediate heart mask in binary format (512x512)
'''
enhanced = img.copy()
for i in range(img.shape[0]):
for j in range(img.shape[1]):
if ch_fg_mask[i][j] == 0:
enhanced[i][j] = 255
retval, initial_lung_mask = cv2.threshold(enhanced, fg_threshold, 255, cv2.THRESH_BINARY_INV)
lung_mask_op1 = opening(initial_lung_mask, square(2))
lung_mask_op1cl1 = closing(lung_mask_op1, disk(8))
lung_mask_op2cl1 = opening(lung_mask_op1cl1, square(8))
lung_mask_op3cl1 = opening(lung_mask_op2cl1, disk(8))
lung_mask = closing(lung_mask_op3cl1, disk(16))
ch_lung_mask = convex_hull_image(lung_mask)
initial_int_heart_mask = ch_lung_mask * np.invert(lung_mask)
int_heart_mask_op1 = opening(initial_int_heart_mask, square(3))
int_heart_mask = opening(int_heart_mask_op1, disk(3))
if showOutput:
fig, axes = plt.subplots(4, 3, sharex=True, sharey=True, figsize=(20, 20))
axes = axes.ravel()
axes[0].set_title('Original Image')
axes[0].imshow(img, cmap='gray')
axes[1].set_title('Enhanced Image')
axes[1].imshow(enhanced, cmap='gray')
axes[2].set_title('Initial Lung Mask')
axes[2].imshow(initial_lung_mask, cmap='gray')
axes[3].set_title('On Opening with Square SE (2)')
axes[3].imshow(lung_mask_op1, cmap='gray')
axes[4].set_title('On Closing with Disk SE (8)')
axes[4].imshow(lung_mask_op1cl1, cmap='gray')
axes[5].set_title('On Opening with Square SE (8)')
axes[5].imshow(lung_mask_op2cl1, cmap='gray')
axes[6].set_title('On Opening with Disk SE (8)')
axes[6].imshow(lung_mask_op3cl1, cmap='gray')
axes[7].set_title('On Closing with Disk SE (16)')
axes[7].imshow(lung_mask, cmap='gray')
axes[8].set_title('Convex Hull of Lung Mask')
axes[8].imshow(ch_lung_mask, cmap='gray')
axes[9].set_title('Initial Intermediate heart mask')
axes[9].imshow(initial_int_heart_mask, cmap='gray')
axes[10].set_title('On Opening with Square SE (3)')
axes[10].imshow(int_heart_mask_op1, cmap='gray')
axes[11].set_title('Intermediate heart mask')
axes[11].imshow(int_heart_mask, cmap='gray')
return lung_mask, ch_lung_mask, int_heart_mask
def chullSpineMask(img, int_heart_mask, showOutput=0):
int_heart_pixel = img.copy()
for i in range(img.shape[0]):
for j in range(img.shape[1]):
if int_heart_mask[i][j] == 0:
int_heart_pixel[i][j] = 0
centroids, segmented_heart_img = imgKMeans(int_heart_pixel, 3, showOutput=0)
spine_threshold = (max(centroids.values()))[0]
retval, initial_spine_mask = cv2.threshold(int_heart_pixel, spine_threshold, 255, cv2.THRESH_BINARY)
spine_mask_cl1 = closing(initial_spine_mask, disk(20))
spine_mask = opening(spine_mask_cl1, square(4))
label_img = label(spine_mask)
spine_regions = regionprops(label_img)
# Center point of the spine
y0, x0 = spine_regions[0].centroid
orientation = spine_regions[0].orientation
# Top-most point of the spine
x2 = x0 - math.sin(orientation) * 0.6 * spine_regions[0].axis_major_length
y2 = y0 - math.cos(orientation) * 0.6 * spine_regions[0].axis_major_length
chull_spine_mask = spine_mask.copy()
# Vertical axis
for i in range(math.ceil(y2), img.shape[1]):
if i > math.ceil(y0):
# Horizontal axis
for j in range(img.shape[0]):
chull_spine_mask[i][j] = 255
else:
# Horizontal axis
for j in range(math.ceil(x0)):
chull_spine_mask[i][j] = 255
heart_mask = int_heart_mask * np.invert(chull_spine_mask)
if showOutput:
fig, axes = plt.subplots(3, 3, sharex=True, sharey=True, figsize=(15, 15))
axes = axes.ravel()
axes[0].set_title('Intermediate Heart Mask')
axes[0].imshow(int_heart_mask, cmap='gray')
axes[1].set_title('Intermediate Heart Segment')
axes[1].imshow(int_heart_pixel, cmap='gray')
axes[2].set_title('Intermediate Heart Segment on K-Means (K = 3)')
axes[2].imshow(segmented_heart_img)
axes[3].set_title('Spine Mask')
axes[3].imshow(initial_spine_mask, cmap='gray')
axes[4].set_title('On Closing with Disk SE (20)')
axes[4].imshow(spine_mask_cl1, cmap='gray')
axes[5].set_title('On Opening with Square SE (4)')
axes[5].imshow(spine_mask, cmap='gray')
axes[6].set_title('Centroid and uppermost point')
axes[6].imshow(spine_mask, cmap='gray')
axes[6].plot((x0, x2), (y0, y2), '-r', linewidth=1.5)
axes[6].plot(x0, y0, '.g', markersize=5)
axes[6].plot(x2, y2, '.b', markersize=5)
axes[7].set_title('Convex Hull of Spine Mask')
axes[7].imshow(chull_spine_mask, cmap='gray')
axes[8].set_title('Heart Mask')
axes[8].imshow(heart_mask, cmap='gray')
return spine_mask, heart_mask
def segmentHeart(img, heart_mask, showOutput=0):
seg_heart = cv2.bitwise_and(img, img, mask=heart_mask)
if showOutput:
plt.figure(figsize=(10, 5))
plt.title('Segmented Heart')
plt.imshow(seg_heart, cmap='gray')
return seg_heart
def segmentLungs(img, lung_mask, showOutput=0):
seg_lungs = cv2.bitwise_and(img, img, mask=lung_mask)
if showOutput:
plt.figure(figsize=(10, 5))
plt.title('Segmented Lungs')
plt.imshow(seg_lungs, cmap='gray')
return seg_lungs
def applyMaskColor(mask, mask_color):
masked = np.concatenate(([mask[ ... , np.newaxis] * color for color in mask_color]), axis=2)
# Matplotlib expects color intensities to range from 0 to 1 if a float
maxValue = np.amax(masked)
minValue = np.amin(masked)
# Therefore, scale the color image accordingly
masked = masked / (maxValue - minValue)
return masked
def getColoredMasks(img, heart_mask, lung_mask, showOutput=0):
heart_mask_color = np.array([256, 0, 0])
lung_mask_color = np.array([0, 256, 0])
heart_colored = applyMaskColor(heart_mask, heart_mask_color)
lung_colored = applyMaskColor(lung_mask, lung_mask_color)
colored_masks = heart_colored + lung_colored
if showOutput:
fig, axes = plt.subplots(2, 2, figsize=(10, 10))
ax = axes.ravel()
ax[0].set_title("Original Image")
ax[0].imshow(img, cmap='gray')
ax[1].set_title("Heart Mask")
ax[1].imshow(heart_colored)
ax[2].set_title("Lung Mask")
ax[2].imshow(lung_colored)
ax[3].set_title("Masks")
ax[3].imshow(colored_masks)
return heart_colored, lung_colored, colored_masks